package models

import (
	"encoding/json"
	"errors"
	"fmt"
	"time"

	"github.com/gomodule/redigo/redis"
)

var (
	redisMaxIdle        int = 3   //最大空闲连接数
	redisIdleTimeoutSec int = 240 //最大空闲连接时间
	redisPool           *redis.Pool
)

// ConnectRedis 初始化redis连接池
func ConnectRedis() {
	redisHost := "172.40.0.95"
	redisPort := 6379
	RedisDB := 0
	redisPassword := "123456"
	redisURL := fmt.Sprintf("redis://%s:%d", redisHost, redisPort)
	redisPool = NewRedisPool(redisURL, redisPassword, RedisDB)
	fmt.Println("使用redis数据库:", RedisDB)
}

// NewRedisPool 返回redis连接池
func NewRedisPool(redisURL, pswd string, db int) *redis.Pool {
	return &redis.Pool{
		MaxIdle:     redisMaxIdle,
		IdleTimeout: time.Duration(redisIdleTimeoutSec) * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.DialURL(redisURL)
			if err != nil {
				err = fmt.Errorf("redis connection error: %s", err)
				fmt.Println(err.Error())
				return nil, err
			}
			//验证redis密码
			if _, err = c.Do("AUTH", pswd); err != nil {
				fmt.Println("验证redis密码失败:", err)
				return nil, fmt.Errorf("redis auth password error: %s", err)
			}
			if _, err = c.Do("SELECT", db); err != nil {
				c.Close()
				return nil, err
			}
			return c, nil
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			if err != nil {

				fmt.Println(err)
				return fmt.Errorf("ping redis error: %s", err)
			}
			return nil
		},
	}
}

// Set 请加表名前缀 比如employee_***
func Set(k string, data interface{}) error {
	c := redisPool.Get()
	defer c.Close()
	value, _ := json.Marshal(data)
	_, err := c.Do("SET", k, value)
	if err != nil {
		return err
	}
	return nil
}

// RedisOpen 打开redis连接
func RedisOpen() redis.Conn {
	return redisPool.Get()
}

// RedisClose 关闭redis连接
func RedisClose(c redis.Conn) {
	c.Close()
}

// Lpush 插入多个
func Lpush(k string, data interface{}) error {
	c := redisPool.Get()
	defer c.Close()
	_, err := c.Do("LPUSH", k, data)
	if err != nil {
		return err
	}
	return nil
}

// LBatchpush 插入多个
func LBatchpush(k string, data interface{}, c redis.Conn) error {
	_, err := c.Do("LPUSH", k, data)
	if err != nil {
		return err
	}
	return nil
}

// PlusOne 值累加1
func PlusBatchOne(k string, c redis.Conn) int {
	if c == nil {
		c = redisPool.Get()
		defer c.Close()
	}
	result, err := redis.Int(c.Do("INCR", k))
	if err != nil {
		return -1
	}
	return result
}

// GetInt 获取数字
func GetInt(k string) (int, error) {

	c := redisPool.Get()
	defer c.Close()
	result, err := redis.Int(c.Do("GET", k))
	if err != nil {
		return 0, err
	}
	return result, nil
}

// Rpush 插入多个
func Rpush(k string, data int) error {

	c := redisPool.Get()
	defer c.Close()
	_, err := c.Do("RPUSH", k, data)
	if err != nil {
		return err
	}
	return nil
}

// RpushString 插入多个Object
func RpushString(k string, data string) (count int, err error) {

	c := redisPool.Get()
	defer c.Close()
	count, err = redis.Int(c.Do("RPUSH", k, data))
	return
}

// LpushString 插入多个Object
func LpushString(k string, data string) (count int, err error) {

	c := redisPool.Get()
	defer c.Close()
	count, err = redis.Int(c.Do("LPUSH", k, data))
	return
}

// LREM 移除
func LREM(k string, count int, value interface{}) (err error) {

	c := redisPool.Get()
	defer c.Close()
	_, err = c.Do("LREM", k, count, value)
	return
}

// LTRIM 只保留左边的多少个
func LTRIM(k string, start, stop int) (err error) {

	c := redisPool.Get()
	defer c.Close()
	_, err = c.Do("LTRIM", k, start, stop)
	return
}

// Lrange 获取
func Lrange(k string, start, end int) ([]int, error) {
	c := redisPool.Get()
	defer c.Close()

	idlist, err := redis.Ints(c.Do("LRANGE", k, start, end))
	if err != nil {
		fmt.Println("Get Error: ", err.Error(), k)
		return nil, err
	}
	return idlist, nil
}

// LrangeString 从左边获取多个
func LrangeString(k string, start, end int) ([]string, error) {
	c := redisPool.Get()
	defer c.Close()
	idlist, err := redis.Strings(c.Do("LRANGE", k, start, end))
	if err != nil {
		fmt.Println("Get Error: ", err.Error(), k)
		return nil, err
	}
	return idlist, nil
}

// LSET 插入多个
func LSET(k string, index int, value string) error {

	c := redisPool.Get()
	defer c.Close()

	_, err := c.Do("LSET", k, index, value)
	if err != nil {
		fmt.Println("Get Error: ", err.Error(), k)
		return err
	}
	return nil
}

// Rrange 从右边获取最新的数据
func Rrange(k string, start, end int) ([]int, error) {

	c := redisPool.Get()
	defer c.Close()

	idlist, err := redis.Ints(c.Do("RRANGE", k, start, end))
	if err != nil {
		fmt.Println("Get Error: ", err.Error(), k)
		return nil, err
	}
	return idlist, nil
}

// GetKeyTTL 获取这个key的剩余过期时间，秒
func GetKeyTTL(k string) int {

	c := redisPool.Get()
	defer c.Close()
	result, err := redis.Int(c.Do("TTL", k))
	if err != nil {
		fmt.Println("GetKeyTTL:", err)
		return 0
	}
	return result
}

func SetKeyExpire(k string, ex int, c redis.Conn) {
	if c == nil {
		c = redisPool.Get()
		defer c.Close()
	}
	_, err := c.Do("EXPIRE", k, ex)
	if err != nil {
		fmt.Println("set error", err.Error())
	}
}
func SetJsonWithExpire(k string, data interface{}, ex int, c redis.Conn) error {
	if c == nil {
		c = redisPool.Get()
		defer c.Close()
	}
	value, err := json.Marshal(data)
	if err != nil {
		return err
	}
	_, err = c.Do("SET", k, value, "ex", ex)

	return err

}
func SetWithExpire(k string, data interface{}, ex int, c redis.Conn) error {
	if c == nil {
		c = redisPool.Get()
		defer c.Close()
	}
	_, err := c.Do("SET", k, data, "ex", ex)

	return err

}

func CheckKey(k string, c redis.Conn) bool {

	if c == nil {
		c = redisPool.Get()
		defer c.Close()
	}
	exist, err := redis.Bool(c.Do("EXISTS", k))
	if err != nil {
		fmt.Println(err)
		return false
	}
	return exist

}

func DelKey(k string, c redis.Conn) error {

	if c == nil {
		c = redisPool.Get()
		defer c.Close()
	}
	_, err := c.Do("DEL", k)
	if err != nil {
		fmt.Println(err)
		return err
	}
	return nil
}

// RedisGet RedisGet
func RedisGet(k string, c redis.Conn) ([]byte, error) {
	if c == nil {
		c = redisPool.Get()
		defer c.Close()
	}
	jsonGet, err := redis.Bytes(c.Do("GET", k))
	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	return jsonGet, nil
}

// SADD 用来向set里面增加
func SADD(k string, fileID int) error {

	c := redisPool.Get()
	defer c.Close()
	_, err := c.Do("SADD", k, fileID)
	if err != nil {
		return err
	}
	return nil
}

// SADDString 用来向set里面增加string
func SADDString(k string, value string) error {

	c := redisPool.Get()
	defer c.Close()
	_, err := c.Do("SADD", k, value)
	if err != nil {
		return err
	}
	return nil
}

// SMEMBERS 获取k的所有memebers
func SMEMBERS(k string) (interface{}, error) {

	c := redisPool.Get()
	defer c.Close()
	result, err := c.Do("SMEMBERS", k)
	if err != nil {
		return nil, err
	}
	return result, nil
}

// ZADD 用来向Zset里面增加
func ZADD(key string, score int, value interface{}, c redis.Conn) error {
	key = fmt.Sprintf("%s", key)

	value, _ = json.Marshal(value)
	_, err := c.Do("ZADD", key, score, value)
	if err != nil {
		return err
	}
	return nil
}

// ZREVRANGEBWITHCORES 递减获取用户的fileid数组
func ZREVRANGEBWITHCORES(key string, from, end int64, desc bool, c redis.Conn) (result []int, err error) {
	key = fmt.Sprintf("%s", key)

	if desc == true {
		result, err = redis.Ints(c.Do("zrevrange", key, from, end, "withscores"))
	} else {
		result, err = redis.Ints(c.Do("zrange", key, from, end, "withscores"))
	}
	if err != nil {
		return nil, err
	}
	return result, nil
}

// ZREMRANGEBYSCORE 移除有序集中，指定分数（score）区间内的所有成员。
func ZREMRANGEBYSCORE(key string, from, end int, c redis.Conn) (result interface{}, err error) {
	key = fmt.Sprintf("%s", key)

	result, err = c.Do("ZREMRANGEBYSCORE", key, from, end, "withscores")

	if err != nil {
		return nil, err
	}
	return result, nil
}

// CacheSetDataPlus  缓存一些数据，每隔一段时间再更新数据库，避免db较大压力
func CacheSetDataPlus(dataname string, dataField string) int {
	k := fmt.Sprintf("cache:%s", dataname)
	c := redisPool.Get()
	defer c.Close()
	result, err := redis.Int(c.Do("HGET", k, dataField))
	if err != nil {
		result = 0
	}
	_, err = c.Do("HSET", k, dataField, result+1)
	if err != nil {
		fmt.Println("CacheSetDataPlus HSET " + dataname + " field:" + dataField)
		return 0
	}
	return result + 1
}

// DianZhan 点赞，点过的就取消点赞
func DianZhan(tablename, channelStr, docid string, userid int, c redis.Conn) int {

	key := fmt.Sprintf("%s:%s:%s", tablename, channelStr, docid)
	result, err := redis.Int(c.Do("GETBIT", key, userid))
	if err != nil {
		fmt.Println("DianZhan： ", err)
		return -1
	}
	if result == 0 {
		result = 1
	} else {
		result = 0
	}
	_, err = c.Do("SETBIT", key, userid, result)
	fmt.Println(key, userid, result, err)

	return result
}

// GetDianZhan 查看某个作品是否已经点赞
func GetDianZhan(tablename, channelStr, docid string, userid int, c redis.Conn) int {

	key := fmt.Sprintf("%s:%s:%s", tablename, channelStr, docid)
	result, err := redis.Int(c.Do("GETBIT", key, userid))
	if err != nil {
		fmt.Println("DianZhan： ", err)
		return -1
	}
	return result
}

// CacheSetData CacheSetData
func CacheSetData(dataname string, dataField string, value int) error {
	k := fmt.Sprintf("cache:%s", dataname)
	c := redisPool.Get()
	defer c.Close()
	_, err := c.Do("HSET", k, dataField, value)
	return err
}

// CacheGetData GetCacheData
func CacheGetData(dataname, dataField string) int {

	k := fmt.Sprintf("cache:%s", dataname)
	c := redisPool.Get()
	defer c.Close()
	result, err := redis.Int(c.Do("HGET", k, dataField))
	if err != nil {
		// fmt.Println("CacheGetData HGET " + dataname + " field:" + dataField)
		return 0
	}
	return result
}

// CacheGetAllKey 获取一个cache中的所有field
func CacheGetAllKey(dataname string) []string {

	k := fmt.Sprintf("cache:%s", dataname)
	c := redisPool.Get()
	defer c.Close()
	result, err := redis.Strings(c.Do("HKEYS", k))
	if err != nil {
		// fmt.Println("CacheGetData HGET " + dataname + " field:" + dataField)
		return nil
	}
	return result
}

func getRedisBytes(labelKey string, c redis.Conn) ([]byte, error) {
	resultKey, err := RedisGet(labelKey, c)
	if err != nil {
		// fmt.Println("GetJsonFromRedisByLabel RedisGet:", err, labelKey)
		return nil, err
	}
	resultkeyStr := string(resultKey)
	if len(resultkeyStr) < 2 {
		fmt.Println("GetJsonFromRedisByLabel key长度不够:", resultkeyStr)
		return nil, errors.New("GetJsonFromRedisByLabel key长度不够")
	}
	resultkeyStr = resultkeyStr[1 : len(resultkeyStr)-1]
	result, err := RedisGet(resultkeyStr, c)
	if err != nil {
		// fmt.Println("GetJsonFromRedisByLabel RedisGet:", err.Error())
		return nil, err
	}
	return result, err
}
